package sk.velkavrana.app.service.impl;

import org.apache.commons.lang.StringUtils;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.Element;
import org.hibernate.dialect.Dialect;
import org.hibernate.mapping.Column;
import org.hibernate.mapping.PrimaryKey;
import org.hibernate.mapping.Table;
import sk.velkavrana.app.model.ConnectionInfo;
import sk.velkavrana.app.model.ImportActions;
import sk.velkavrana.app.service.ImportService;
import sk.velkavrana.app.service.sharedData.ImportDataListener;
import sk.velkavrana.app.service.sharedData.ProgressEvent;
import sk.velkavrana.app.util.DBUtil;
import sk.velkavrana.app.util.DBUtilV2;
import sk.velkavrana.app.util.ImportUtil;

import java.io.File;
import java.sql.Connection;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * @author mtymes
 * @since May 15, 2009 5:39:26 PM
 */
public class ImportServiceImpl implements ImportService {

    private Object createObject(String className) {

        Object returnValue = null;

        try {
            Class beanClass = Class.forName(className);

            returnValue = beanClass.newInstance();

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }

        return returnValue;
    }


    private Table generateTable(String tableName,
                                String schemaName,
                                Document doc,
                                Dialect dialect) {

        Table table = new Table(tableName);

        if (StringUtils.isNotBlank(schemaName)) {
            table.setSchema(schemaName);
        }

        PrimaryKey primaryKey = null;

        List<Element> elements = (List<Element>) doc.selectNodes("//tableinfo/colinfo");

        Column column;
        for (Element element : elements) {
            column = new Column();
            column.setName(element.attribute("name").getValue());

            int sqlTypeCode = Integer.parseInt(element.attribute("sqltype").getValue());
            column.setSqlTypeCode(sqlTypeCode);

            int scale = Integer.parseInt(element.attribute("scale").getValue());
            column.setScale(scale);

            int precision = Integer.parseInt(element.attribute("precision").getValue());
            column.setPrecision(precision);

            column.setNullable(Boolean.parseBoolean(element.attribute("isNull").getValue()));

            // todo: is length set properly
            int length = precision;
            String sqlType = dialect.getTypeName(sqlTypeCode, length, precision, scale);
            column.setSqlType(sqlType);

            boolean isPrimary = Boolean.parseBoolean(element.attribute("isPrimary").getValue());
            if (isPrimary) {
                if (primaryKey == null) {
                    primaryKey = new PrimaryKey();
                }

                primaryKey.addColumn(column);
            }

            table.addColumn(column);
        }

        if (primaryKey != null) {
            table.setPrimaryKey(primaryKey);
        }

        return table;
    }


    private void importFile(ConnectionInfo connectionInfo,
                            Connection conn,
                            ImportActions actions,
                            String fileName,
                            String directory,
                            ImportDataListener listener) {

        listener.updateStatus("creating DOM from file: " + fileName);

        Document doc = ImportUtil.getDocument(directory + File.separator + fileName);

        if (doc != null) {
            String[] tableIds = ((Attribute) doc.selectSingleNode("//dbtable/@name")).getValue().split("\\.");
            String tableName = tableIds[tableIds.length - 1];
//                    (StringUtils.isBlank(connectionInfo.getSchema()) ?  "" : connectionInfo.getSchema() + ".") +
//                            tableIds[tableIds.length - 1];
            String schemaName = connectionInfo.getSchema();


            Table table = null;
            Dialect dialect = (Dialect) createObject(connectionInfo.getDialectClassName());

            if (actions.isDropTable()) {
                if (table == null) {
                    table = generateTable(tableName, schemaName, doc, dialect);
                }

                listener.updateStatus("droping table: " + tableName);

                String query = table.sqlDropString(dialect, null, connectionInfo.getSchema());
                // todo: do something it will return false
                DBUtilV2.executeUpdate(conn, query);
            }

            if (actions.isCreateTable()) {
                if (table == null) {
                    table = generateTable(tableName, schemaName, doc, dialect);
                }

                listener.updateStatus("creating table: " + tableName);

                String query = table.sqlCreateString(dialect, null, null, connectionInfo.getSchema());
                // todo: do something it will return false
                DBUtilV2.executeUpdate(conn, query);
            }

            tableName = (StringUtils.isBlank(schemaName) ? "" : (schemaName + ".")) + tableName;

            if (actions.isClearData()) {
                listener.updateStatus("deleting content of table: " + tableName);
                DBUtilV2.executeUpdate(conn, "DELETE FROM " + tableName);
            }

            if (actions.isImportData()) {
                // process rows
                listener.updateStatus("getting information about columns from table: " + tableName);
                Map<String, Integer> colTypes = ImportUtil.getColTypes((Element) doc.selectSingleNode("//tableinfo"));
                listener.updateStatus("reading row data from file: " + fileName);
                List<Element> rows = (List<Element>) doc.selectNodes("//row");

                listener.updateStatus("filling table: " + tableName);
                for (int i = 0; i < rows.size(); i++) {
                    Element row = rows.get(i);
                    listener.rowProgressUpdate(new ProgressEvent(i, rows.size()));

                    DBUtilV2.insertRecord(conn, tableName, ImportUtil.getRowData(row, colTypes), colTypes);
                }
                listener.rowProgressUpdate(new ProgressEvent(rows.size(), rows.size()));
            }
        } else {
            // todo: send error
        }
    }


    public void importData(ConnectionInfo connectionInfo,
                           ImportActions actions,
                           Set<String> files,
                           String directory,
                           ImportDataListener listener) {

        Connection conn = DBUtil.connectSilently(connectionInfo);

        if (conn != null) {
            try {
                int index = 0;
                listener.fileProgressUpdate(new ProgressEvent(index++, files.size()));
                for (String fileName : files) {

                    importFile(connectionInfo, conn, actions, fileName, directory, listener);

                    listener.fileProgressUpdate(new ProgressEvent(index++, files.size()));
                }
            } finally {
                DBUtil.closeSilently(conn);
            }
        } else {
            listener.updateStatus("unable to connect to database");
        }
    }
}
